package net.sk.china.front.strategy;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import net.sk.china.common.exception.TalkException;
import net.sk.china.common.model.user.User;
import net.sk.china.common.utils.DateUtil;
import net.sk.china.common.utils.RedisUtil;
import net.sk.china.common.utils.TalkUtil;
import net.sk.china.front.constants.Constants;
import net.sk.china.front.constants.RegularConstants;
import net.sk.china.front.request.LoginRequest;
import net.sk.china.front.request.PersonRegisterRequest;
import net.sk.china.front.request.RetrievePwdRequest;
import net.sk.china.front.response.LoginResponse;
import net.sk.china.front.response.ValidateCodeResponse;
import net.sk.china.front.service.user.UserService;
import net.sk.china.front.utils.ValidateCodeUtil;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

/**
 * +----------------------------------------------------------------------
 * // | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
 * // +----------------------------------------------------------------------
 * // | Copyright (c) 2018-2024 聊来 All rights reserved.
 * // +----------------------------------------------------------------------
 * // | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">apache</a> )
 * // +----------------------------------------------------------------------
 * // | @Author: 聊来 <18970881148@qq.com>
 * // +----------------------------------------------------------------------
 * // | DateTime: 2024/6/4 15:50
 * // +----------------------------------------------------------------------
 */

@Component
public class PcService implements UserInterface {

    private static final Logger logger = LoggerFactory.getLogger(PcService.class);

    private final RedisUtil redisUtil;

    private final UserService userService;

    private final RedissonClient redissonClient;

    private final SmsService smsService;

    public PcService(RedisUtil redisUtil, UserService userService, RedissonClient redissonClient, SmsService smsService) {
        this.redisUtil = redisUtil;
        this.userService = userService;
        this.redissonClient = redissonClient;
        this.smsService = smsService;
    }

    /**
     * 账号、密码、验证码登录
     * @param request 请求对象
     * @param ip 客户端IP
     * @return {@code LoginResponse}
     */
    @Override
    public LoginResponse login(LoginRequest request, String ip) {
        if (StrUtil.isBlank(request.getAccount())) {
            throw new TalkException("账号不能为空");
        }
        if (StrUtil.isBlank(request.getPassword())) {
            throw new TalkException("密码不能为空");
        }
        if (!ReUtil.isMatch(RegularConstants.PASSWORD, request.getPassword())) {
            throw new TalkException("密码格式错误，密码必须以字母开头，长度在6~18之间，只能包含字符、数字和下划线");
        }

        if (StrUtil.isBlank(request.getCaptcha())) {
            throw new TalkException("请输入验证码");
        }

        String code = redisUtil.get(getRedisKey(request.getKey()));
        if (!redisUtil.exists(getRedisKey(request.getKey()))) {
            throw new TalkException("验证码KEY错误或验证码已失效");
        }

        if (!code.equals(request.getCaptcha())) {
            throw new TalkException("验证码不正确");
        }

        User user = userService.getByAccount(request.getAccount());
        if (ObjectUtil.isNull(user)) {
            throw new TalkException("此账号未注册");
        }
        if (user.getStatus().equals(Constants.NUM_ZERO)) {
            throw new TalkException("当前账户已禁用，请联系平台客服人员！");
        }

        // 校验密码
        String password = TalkUtil.encryptPassword(request.getPassword(), request.getAccount());
        if (!user.getPwd().equals(password)) {
            throw new TalkException("密码错误");
        }


        LoginResponse loginResponse = new LoginResponse();
        try {
            // 生成token
            String token = userService.createToken(user.getUid());
            loginResponse.setToken(token);
        } catch (Exception e) {
            logger.error(String.format("生成用户Token失败: %s", e.getMessage()));
        }

        loginResponse.setType("login");

        // 更新用户登录信息
        UpdateWrapper<User> query = new UpdateWrapper<>();
        query.set("last_ip", ip).set("last_login_time", DateUtil.getNowTime()).set("login_type", request.getLoginType());
        query.eq("uid", user.getUid());
        userService.update(query);

        return loginResponse;
    }

    /**
     * 个人注册
     * @param request 请求对象
     * @param ip 客户端IP
     * @return {@code LoginResponse}
     */
    public LoginResponse personRegister(PersonRegisterRequest request, String ip) {
        if (!request.getPassword().equals(request.getCheckPassword())) {
            throw new TalkException("两次输入的密码不一致");
        }

        LoginResponse response = new LoginResponse();

        RLock lock = redissonClient.getLock("pcUserRegister");

        try {
            boolean isLock = lock.tryLock(10, TimeUnit.SECONDS);
            if (!isLock) {
                throw new TalkException("获取锁失败，无法完成注册操作");
            }

            smsService.checkValidateCode(request.getPhone(), "pcRegister", request.getCaptcha());
            User data = userService.getByAccount(request.getPhone());
            if (ObjectUtil.isNotNull(data)) {
                throw new TalkException("您已注册，请勿重新注册");
            }


            LocalDateTime time = DateUtil.getNowTime();

            User user = new User();
            user.setAccount(request.getPhone());
            user.setPwd(TalkUtil.encryptPassword(request.getPassword(), request.getPhone()));
            user.setPhone(request.getPhone());
            user.setUserType("pc");
            user.setLoginType("pc");
            user.setCardId(request.getCardId());
            user.setIdCardReverse(request.getCardReverse());
            user.setIdCardPositive(request.getCardPositive());
            user.setRealName(request.getRealName());
            user.setNickname(request.getRealName());
            user.setAvatar(userService.getAvatar());
            user.setAddress(request.getAddress());
            user.setCreateTime(time);
            user.setLastLoginTime(time);
            user.setLastIp(ip);
            user.setAddIp(ip);

            if (!userService.save(user)) {
                throw new TalkException("用户注册失败");
            }

            redisUtil.delete(Constants.SMS_VALIDATE_PHONE + "pcRegister" + ":" +  request.getPhone());
            response.setType("register");
            response.setToken(userService.createToken(user.getUid()));

        } catch (Throwable e) {
            throw new TalkException(String.format("注册异常：%s", e.getMessage()));
        } finally {
            // 释放锁
            lock.unlock();
        }
        return response;
    }

    /**
     * 找回密码并登录
     * @param request 请求对象
     * @param ip 客户端IP
     * @return {@code LoginResponse}
     */
    public LoginResponse retrievePassword(RetrievePwdRequest request, String ip) {
        User user = userService.getByAccount(request.getPhone());
        if (ObjectUtil.isNull(user)) {
            throw new TalkException("该手机号还未注册过，无法进行找回密码操作");
        }

        if (!request.getPassword().equals(request.getCheckPassword())) {
            throw new TalkException("两次输入的密码不一致");
        }

        // 检测手机验证码
        smsService.checkValidateCode(request.getPhone(), "pcRetrieve", request.getCaptcha());

        UpdateWrapper<User> query = new UpdateWrapper<>();
        query.eq("uid", user.getUid());
        query.set("login_type", "pc").set("last_ip", ip).set("last_login_time", DateUtil.getNowTime()).set("pwd", TalkUtil.encryptPassword(request.getPassword(), request.getPhone()));
        if (!userService.update(query)) {
            logger.error(String.format("找回密码并登录失败，uid=%d", user.getUid()));
        }

        LoginResponse loginResponse = new LoginResponse();
        // 生成token
        try {
            redisUtil.delete(Constants.SMS_VALIDATE_PHONE + "pcRetrieve" + ":" +  request.getPhone());
            String token = userService.createToken(user.getUid());
            loginResponse.setToken(token);
        } catch (Exception e) {
            logger.error(String.format("生成用户Token失败: %s", e.getMessage()));
        }
        loginResponse.setType("login");

        return loginResponse;
    }

    /**
     * 获取图片验证码
     * @return {@code ValidateCodeResponse}
     */
    public ValidateCodeResponse getValidateCode() {
        ValidateCodeUtil.Validate randomCode = ValidateCodeUtil.getRandomCode();
        if (ObjectUtil.isNull(randomCode)) { return null; }

        String value = randomCode.getValue().toLowerCase();
        String md5Key = SecureUtil.md5(value);
        String redisKey = getRedisKey(md5Key);
        // 设置5分钟后过期
        redisUtil.set(redisKey, value, 5L, TimeUnit.MINUTES);

        String base64Str = randomCode.getBase64Str();

        ValidateCodeResponse response = new ValidateCodeResponse();
        response.setKey(md5Key);
        response.setCode(TalkUtil.getBase64Image(base64Str));

        return response;
    }

    /**
     * 获取redis key
     * @param md5Key value的md5加密值
     * @return {@code String}
     */
    private String getRedisKey(String md5Key) {
        return Constants.VALIDATE_REDIS_KEY_PREFIX + md5Key;
    }
}
